바운디드 컨텍스트 간 의존성 루프의 치명성
바운디드 컨텍스트 간의 의존성 루프(Circular Dependency)는 원칙적으로 권장되지 않으며, 변경 전파 복잡성과 유지보수 비용 증가 등의 부작용을 가져올 수 있습니다. 그러나 경우에 따라 바운디드 컨텍스트 수준에서의 루프가 반드시 치명적이지 않을 수도 있습니다.
바운디드 컨텍스트 간 루프가 치명적이지 않은 이유
바운디드 컨텍스트는 주로 비즈니스 관점의 논리적 경계를 나타내며, 기술적 관점에서의 직접적인 코드나 패키지 의존성을 의미하지 않습니다. 따라서 두 컨텍스트가 서로 논리적(semantic) 으로만 서로를 참조하거나, 서로 다른 상황에서 서로 다른 방식으로 참조하는 등, 엄격히 관리되는 조건 아래에서는 큰 문제가 되지 않을 수 있습니다.
즉, 다음과 같은 조건이라면 치명적이지 않습니다.
-
컨텍스트 간 통신이 비동기 이벤트 기반일 때
- 이벤트를 발행하는 쪽과 소비하는 쪽이 서로를 직접적인 코드 참조하지 않기 때문에 결합도가 낮아집니다.
-
컨텍스트가 서로 다른 수준이나 목적으로 참조를 하고 있고, 이 의존성이 엄격히 읽기 전용(CQRS의 조회 모델)일 때
- 단방향적 데이터 흐름을 강제하므로 변경이 전파되는 복잡성을 낮출 수 있습니다.
-
두 컨텍스트 간 참조하는 데이터의 변경 빈도가 극도로 낮거나, 안정적인 인터페이스를 가지고 있을 때
- 변경이 거의 발생하지 않으면, 의존성 루프의 영향은 최소화됩니다.
이러한 조건하에서 관리 가능한 루프는 반드시 치명적이지 않습니다.
하지만 가능한 한 루프를 해소하는 것이 바람직합니다.
의존성 루프는 일반적으로 다음과 같은 문제를 야기할 가능성이 높습니다:
-
한 컨텍스트의 변경이 루프를 통해 다시 돌아오며 순환적인 변경 전파를 야기할 수 있음
-
컨텍스트 간 경계를 명확히 분리하기 어려워지고, 컨텍스트의 독립적 발전을 저해함
따라서 아래와 같은 방법으로 루프를 적극적으로 해소하는 것이 권장됩니다.
의존성 루프 해소를 위한 전략적 대안
✅ 1. Context 분리 및 재구성
의존성이 루프가 발생하는 이유는 보통 두 컨텍스트가 서로에 대한 책임을 공유하기 때문입니다.
책임을 명확히 나눠서 중간에 새로운 컨텍스트를 도입하면 루프를 끊을 수 있습니다.
예시: AdminContext의 책임을 재배치
- 기존의 문제:
UserContext --> PaymentContext --> AdminContext --> UserContext (루프 발생)
- 해결책: AdminContext를 더 세부적 책임으로 나눕니다.
UserContext --> PaymentContext --> ReportingContext (NEW)
UserContext --> ReportingContext
이때 AdminContext
대신 조회/정산/통계 역할만을 하는 ReportingContext
를 별도 생성해
UserContext
를 조회 모델(CQRS 읽기 전용)로 참조하게 합니다.
결과적으로 루프가 제거됩니다.
UserContext --> PaymentContext --> ReportingContext
✅ 2. CQRS 패턴 적극 활용
각 컨텍스트가 필요한 다른 컨텍스트의 데이터에 접근할 때, 직접적인 참조를 줄이고 별도의 Read 모델을 활용하면 루프가 제거됩니다.
예시: UserContext 조회모델을 AdminContext 내부로 복제
- 기존 구조
AdminContext --> UserContext
- 변경 후 구조(CQRS)
AdminContext --> UserReadModel(내부조회 모델) <-- UserContext (이벤트 기반 동기화)
-
UserContext가 User 변경사항 이벤트를 발행하면 AdminContext는 이벤트를 수신하여 내부 User조회 모델을 업데이트 합니다.
-
AdminContext가 UserContext를 직접적으로 참조할 필요가 사라져 루프가 해소됩니다.
✅ 3. Event-Driven Architecture (EDA) 도입
바운디드 컨텍스트 간 모든 통신을 이벤트 중심으로 변경하면 컨텍스트는 서로 직접적인 코드 참조를 하지 않게 됩니다.
예를 들어,
-
UserCreated
,PaymentCompleted
,TicketIssued
와 같은 이벤트를 통해 데이터를 동기화 -
각 컨텍스트는 발행된 이벤트만 수신하며 독립적으로 데이터를 관리하고, 직접 API 호출이나 강력한 의존성을 피합니다.
이러한 이벤트 기반 설계를 적용하면 전체적인 결합도가 낮아지고, 루프의 영향이 최소화됩니다.
정리 및 권장 사항
-
의존성 루프는 가능하면 제거하는 것이 좋습니다.
-
반드시 제거가 어렵다면, 명확한 이벤트 기반 통신 방식과 **조회모델 분리(CQRS)**를 통해 영향도를 최소화합니다.
-
책임을 명확히 나누고 중간 컨텍스트를 도입하여 직접적인 순환 의존을 피하는 설계를 추천합니다.
이러한 전략을 통해 시스템 전체의 유지보수성 및 확장성을 현저히 높일 수 있습니다.